﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Loki.Data;
using NUnit.Framework;

namespace Luna.Data.Test
{
    public abstract class EntityTest<TService, TEntity, TKey> : DataTest
        where TService : class
        where TEntity : class, IDataEntity<Guid>
    {
        protected EntityTest(string contextType)
            : base(contextType)
        {
        }

        public abstract Action<TKey> Delete { get; }

        public abstract Predicate<TEntity> DeletePredicate { get; }

        public abstract Func<TKey, TEntity> Get { get; }

        public abstract Func<IEnumerable<TEntity>> GetAll { get; }

        public abstract Func<TEntity, TKey> KeyGetter { get; }

        public abstract Action<TEntity> Save { get; }

        public TService Service
        {
            get
            {
                return Context.Get<TService>();
            }
        }

        public override void SetUp()
        {
            base.SetUp();

            if (Provider == "LocalSqlServer")
            {
                if (File.Exists("TestData.mdf"))
                {
                    File.Delete("TestData.mdf");
                }

                if (File.Exists("TestData_log.ldf"))
                {
                    File.Delete("TestData_log.ldf");
                }
            }

            if (Provider == "LocalSqlServer")
            {
                var configManager = Context.Get<IRepositoryManager>();
                configManager.Create("TestData.mdf");
            }
        }

        public override void TearDown()
        {
            var configManager = Context.Get<IRepositoryManager>();
            configManager.Disconnect();

            if (Provider == "LocalSqlServer")
            {
                if (File.Exists("TestData.mdf"))
                {
                    File.Delete("TestData.mdf");
                }

                if (File.Exists("TestData_log.ldf"))
                {
                    File.Delete("TestData_log.ldf");
                }
            }

            base.TearDown();
        }

        [TearDown]
        public virtual void Clean()
        {
            foreach (var item in GetAll().Where(x => DeletePredicate(x)))
            {
                Delete(KeyGetter(item));
            }
        }

        protected abstract TEntity Create();

        protected abstract bool AreSame(TEntity data1, TEntity data2);

        [Test]
        public void TestInsertAndDelete()
        {
            int countBefore = GetAll().Count();
            TEntity entity1 = Create();
            TEntity entity2 = Create();
            Save(entity1);
            Save(entity2);
            Delete(KeyGetter(entity1));

            int countAfter = GetAll().Count();
            Expect(countAfter - countBefore, Is.EqualTo(1));

            TEntity readEntity = Get(KeyGetter(entity1));
            Expect(readEntity, Is.Null);
        }

        [Test]
        public void TestInsertAndGet()
        {
            TEntity entity = Create();
            Save(entity);
            TEntity readEntity = Get(KeyGetter(entity));
            Expect(AreSame(entity, readEntity), Is.True);
        }

        [Test]
        public void TestInsertAndGetAll()
        {
            int countBefore = GetAll().Count();
            TEntity entity1 = Create();
            TEntity entity2 = Create();
            TEntity modifiedEntity = Create();
            modifiedEntity.PK = entity1.PK;
            modifiedEntity.AlreadyPersisted = true;
            Save(entity1);
            Save(entity2);
            Save(modifiedEntity);

            int countAfter = GetAll().Count();
            Expect(countAfter - countBefore, Is.EqualTo(2));
        }

        [Test]
        public void TestUpdateAndGet()
        {
            TEntity entity = Create();
            TEntity entityModified = Create();
            entityModified.PK = entity.PK;
            Save(entityModified);

            TEntity readEnitty = Get(KeyGetter(entity));
            Expect(AreSame(entityModified, readEnitty), Is.True);
        }
    }
}